// File:    testsi.cpp
// Library: SimpleIni
// Author:  Brodie Thiesfield <code@jellycan.com>
// Source:  http://code.jellycan.com/simpleini/
//
// Demo of usage

#ifdef _WIN32
#pragma warning(disable : 4786)
#endif

#include <locale.h>
#include <stdio.h>
#include <cassert>

#define SI_SUPPORT_IOSTREAMS
#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
#include <fstream>
#endif

//#define SI_CONVERT_GENERIC
//#define SI_CONVERT_ICU
//#define SI_CONVERT_WIN32
#include "SimpleIni.h"

#ifdef SI_CONVERT_ICU
// if converting using ICU then we need the ICU library
#pragma comment(lib, "icuuc.lib")
#endif

#ifdef _WIN32
#include <tchar.h>
#else  // !_WIN32
#define TCHAR char
#define _T(x) x
#define _tprintf printf
#define _tmain main
#endif  // _WIN32

static void Test(CSimpleIni &ini) {
   const TCHAR *pszSection = 0;
   const TCHAR *pItem      = 0;
   const TCHAR *pszVal     = 0;

   // get the value of the key "foo" in section "standard"
   bool bHasMulti;
   pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti);
   _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"), pszVal ? pszVal : _T("(null)"), bHasMulti);

   // set the value of the key "foo" in section "standard"
   ini.SetValue(_T("standard"), _T("foo"), _T("wibble"));
   pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti);
   _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"), pszVal ? pszVal : _T("(null)"), bHasMulti);

   // get all values of the key "foo" in section "standard"
   CSimpleIni::TNamesDepend values;
   if (ini.GetAllValues(_T("standard"), _T("foo"), values)) {
      _tprintf(_T("\n-- Values of standard::foo are:\n"));
      CSimpleIni::TNamesDepend::const_iterator i = values.begin();
      for (; i != values.end(); ++i) {
         pszVal = i->pItem;
         _tprintf(_T("   -> '%s'\n"), pszVal);
      }
   }

   // get the size of the section [standard]
   _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), ini.GetSectionSize(_T("standard")));

   // delete the key "foo" in section "standard", if it has value "bar"
   ini.DeleteValue(_T("standard"), _T("foo"), _T("bar"));
   pszVal = ini.GetValue(_T("standard"), _T("foo"), 0);
   _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"), pszVal ? pszVal : _T("(null)"));

   // delete the key "foo" in section "standard"
   ini.Delete(_T("standard"), _T("foo"));
   pszVal = ini.GetValue(_T("standard"), _T("foo"), 0);
   _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"), pszVal ? pszVal : _T("(null)"));

   // get the size of the section [standard]
   _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), ini.GetSectionSize(_T("standard")));

   // get the list of all key names for the section "standard"
   _tprintf(_T("\n-- Dumping keys of section: [standard]\n"));
   CSimpleIni::TNamesDepend keys;
   ini.GetAllKeys(_T("standard"), keys);

   // dump all of the key names
   CSimpleIni::TNamesDepend::const_iterator iKey = keys.begin();
   for (; iKey != keys.end(); ++iKey) {
      pItem = iKey->pItem;
      _tprintf(_T("Key: %s\n"), pItem);
   }

   // add a decimal value
   ini.SetLongValue(_T("integer"), _T("dec"), 42, NULL, false);
   ini.SetLongValue(_T("integer"), _T("hex"), 42, NULL, true);

   // add some bool values
   ini.SetBoolValue(_T("bool"), _T("t"), true);
   ini.SetBoolValue(_T("bool"), _T("f"), false);

   // get the values back
   assert(42 == ini.GetLongValue(_T("integer"), _T("dec")));
   assert(42 == ini.GetLongValue(_T("integer"), _T("hex")));
   assert(true == ini.GetBoolValue(_T("bool"), _T("t")));
   assert(false == ini.GetBoolValue(_T("bool"), _T("f")));

   // delete the section "standard"
   ini.Delete(_T("standard"), NULL);
   _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), ini.GetSectionSize(_T("standard")));

   // iterate through every section in the file
   _tprintf(_T("\n-- Dumping all sections\n"));
   CSimpleIni::TNamesDepend sections;
   ini.GetAllSections(sections);
   CSimpleIni::TNamesDepend::const_iterator iSection = sections.begin();
   for (; iSection != sections.end(); ++iSection) {
      pszSection = iSection->pItem;

      // print the section name
      printf("\n");
      if (*pszSection) {
         _tprintf(_T("[%s]\n"), pszSection);
      }

      // if there are keys and values...
      const CSimpleIni::TKeyVal *pSectionData = ini.GetSection(pszSection);
      if (pSectionData) {
         // iterate over all keys and dump the key name and value
         CSimpleIni::TKeyVal::const_iterator iKeyVal = pSectionData->begin();
         for (; iKeyVal != pSectionData->end(); ++iKeyVal) {
            pItem  = iKeyVal->first.pItem;
            pszVal = iKeyVal->second;
            _tprintf(_T("%s=%s\n"), pItem, pszVal);
         }
      }
   }
}

#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
static bool TestStreams(const TCHAR *a_pszFile, bool a_bIsUtf8, bool a_bUseMultiKey, bool a_bUseMultiLine) {
   // load the file
   CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
   _tprintf(_T("Loading file: %s\n"), a_pszFile);
   std::ifstream instream;
   instream.open(a_pszFile, std::ifstream::in | std::ifstream::binary);
   SI_Error rc = ini.LoadData(instream);
   instream.close();
   if (rc < 0) {
      printf("Failed to open file.\n");
      return false;
   }

   Test(ini);

   // save the file (simple)
   _tprintf(_T("\n-- Saving file to: testsi-out-streams.ini\n"));
   std::ofstream outstream;
   outstream.open("testsi-out-streams.ini", std::ofstream::out | std::ofstream::binary);
   ini.Save(outstream);
   outstream.close();

   return true;
}
#endif  // SI_SUPPORT_IOSTREAMS

static bool TestFile(const TCHAR *a_pszFile, bool a_bIsUtf8, bool a_bUseMultiKey, bool a_bUseMultiLine) {
   // load the file
   CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
   _tprintf(_T("Loading file: %s\n"), a_pszFile);
   SI_Error rc = ini.LoadFile(a_pszFile);
   if (rc < 0) {
      printf("Failed to open file.\n");
      return false;
   }

   // run the tests
   Test(ini);

   // save the file (simple)
   _tprintf(_T("\n-- Saving file to: testsi-out.ini\n"));
   ini.SaveFile("testsi-out.ini");

   // save the file (with comments)
   // Note: to save the file and add a comment to the beginning, use
   // code such as the following.
   _tprintf(_T("\n-- Saving file to: testsi-out-comment.ini\n"));
   FILE *fp = NULL;
#if __STDC_WANT_SECURE_LIB__
   fopen_s(&fp, "testsi-out-comment.ini", "wb");
#else
   fp = fopen("testsi-out-comment.ini", "wb");
#endif
   if (fp) {
      CSimpleIni::FileWriter writer(fp);
      if (a_bIsUtf8) {
         writer.Write(SI_UTF8_SIGNATURE);
      }

      // add a string to the file in the correct text format
      CSimpleIni::Converter convert = ini.GetConverter();
      convert.ConvertToStore(_T("; output from testsi.cpp test program") SI_NEWLINE SI_NEWLINE);
      writer.Write(convert.Data());

      ini.Save(writer, false);
      fclose(fp);
   }

   return true;
}

static bool ParseCommandLine(int argc, TCHAR *argv[], const TCHAR *&a_pszFile, bool &a_bIsUtf8, bool &a_bUseMultiKey,
                             bool &a_bUseMultiLine) {
   a_pszFile       = 0;
   a_bIsUtf8       = false;
   a_bUseMultiKey  = false;
   a_bUseMultiLine = false;
   for (--argc; argc > 0; --argc) {
      if (argv[argc][0] == '-') {
         switch (argv[argc][1]) {
            case TCHAR('u'):
               a_bIsUtf8 = true;
               break;
            case TCHAR('m'):
               a_bUseMultiKey = true;
               break;
            case TCHAR('l'):
               a_bUseMultiLine = true;
               break;
         }
      } else {
         a_pszFile = argv[argc];
      }
   }
   if (!a_pszFile) {
      _tprintf(
          _T("Usage: testsi [-u] [-m] [-l] iniFile\n")
          _T("   -u  Load file as UTF-8 (Default is to use system locale)\n")
          _T("   -m  Enable multiple keys\n")
          _T("   -l  Enable multiple line values\n"));
      return false;
   }

   return true;
}

extern bool TestStreams();

int _tmain(int argc, TCHAR *argv[]) {
   setlocale(LC_ALL, "");

   // start of automated testing...
   TestStreams();

   // parse the command line
   const TCHAR *pszFile;
   bool bIsUtf8, bUseMultiKey, bUseMultiLine;
   if (!ParseCommandLine(argc, argv, pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
      return 1;
   }

   // run the test
   if (!TestFile(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
      return 1;
   }
#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
   if (!TestStreams(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
      return 1;
   }
#endif

   return 0;
}
